home *** CD-ROM | disk | FTP | other *** search
/ Mac-Source 1994 July / Mac-Source_July_1994.iso / C and C++ / Entertainment / MacMud / Mud 4.0 / ed.c < prev    next >
Encoding:
Text File  |  1993-03-17  |  56.9 KB  |  2,521 lines  |  [TEXT/MPS ]

  1. /*
  2.  *  ed - standard editor
  3.  *  ~~
  4.  *    Authors: Brian Beattie, Kees Bot, and others
  5.  *
  6.  * Copyright 1987 Brian Beattie Rights Reserved.
  7.  * Permission to copy or distribute granted under the following conditions:
  8.  * 1). No charge may be made other than reasonable charges for reproduction.
  9.  * 2). This notice must remain intact.
  10.  * 3). No further restrictions may be added.
  11.  * 4). Except meaningless ones.
  12.  *
  13.  * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  14.  *  TurboC mods and cleanup 8/17/88 RAMontante.
  15.  *  Further information (posting headers, etc.) at end of file.
  16.  *  RE stuff replaced with Spencerian version, sundry other bugfix+speedups
  17.  *  Ian Phillipps. Version incremented to "5".
  18.  * _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
  19.  *
  20.  *  ********--->> INDENTATION ONLINE !!!! <<----------****************
  21.  *  Indentation added by Ted Gaunt (aka Qixx) paradox@mcs.anl.gov
  22.  *  help files added by Ted Gaunt
  23.  *  '^' added by Ted Gaunt
  24.  *  Note: this version compatible with v.3.0.34  (and probably all others too)
  25.  *  but i've only tested it on v.3 please mail me if it works on your mud
  26.  *  and if you like it!
  27.  * _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
  28.  *
  29.  *  Online indentation algorithm replaced with adapted version from DGD
  30.  *  editor by Dworkin, 920510
  31.  */
  32.  
  33.  
  34. int    version = 5;    /* used only in the "set" function, for i.d. */
  35.  
  36. #include <stdio.h>
  37. #include <string.h>
  38. #include <types.h>
  39. #include <mac.h>
  40. #include <ctype.h>
  41. #include <sys/types.h>
  42. /* Regexp is Henry Spencer's package. WARNING: regsub is modified to return
  43.  * a pointer to the \0 after the destination string, and this program refers
  44.  * to the "private" reganch field in the struct regexp.
  45.  */
  46. #include "lint.h"
  47. #include "regexp.h"
  48. #include "interpret.h"
  49. #include "object.h"
  50. #include "config.h"
  51. #include "exec.h"
  52. #include "comm.h"
  53. /*
  54.  *    #defines for non-printing ASCII characters
  55.  */
  56. #define NUL    0x00    /* ^@ */
  57. #define EOS    0x00    /* end of string */
  58. #define SOH    0x01    /* ^A */
  59. #define STX    0x02    /* ^B */
  60. #define ETX    0x03    /* ^C */
  61. #define EOT    0x04    /* ^D */
  62. #define ENQ    0x05    /* ^E */
  63. #define ACK    0x06    /* ^F */
  64. #define BEL    0x07    /* ^G */
  65. #define BS    0x08    /* ^H */
  66. #define HT    0x09    /* ^I */
  67. #define LF    0x0a    /* ^J */
  68. #define NL    '\n'
  69. #define VT    0x0b    /* ^K */
  70. #define FF    0x0c    /* ^L */
  71. #define CR    0x0d    /* ^M */
  72. #define SO    0x0e    /* ^N */
  73. #define SI    0x0f    /* ^O */
  74. #define DLE    0x10    /* ^P */
  75. #define DC1    0x11    /* ^Q */
  76. #define DC2    0x12    /* ^R */
  77. #define DC3    0x13    /* ^S */
  78. #define DC4    0x14    /* ^T */
  79. #define NAK    0x15    /* ^U */
  80. #define SYN    0x16    /* ^V */
  81. #define ETB    0x17    /* ^W */
  82. #define CAN    0x18    /* ^X */
  83. #define EM    0x19    /* ^Y */
  84. #define SUB    0x1a    /* ^Z */
  85. #define ESC    0x1b    /* ^[ */
  86. #define FS    0x1c    /* ^\ */
  87. #define GS    0x1d    /* ^] */
  88. /*#define RS    0x1e       ^^ */
  89. #define US    0x1f    /* ^_ */
  90. #define SP    0x20    /* space */
  91. #define DEL    0x7f    /* DEL*/
  92. #define ESCAPE  '\\'
  93.  
  94. #define TAB '\t'        /* added by Qixx for indentation */
  95. #define LB '{'
  96. #define RB '}'
  97. #define LC '('
  98. #define RC ')'
  99. #define LS '['
  100. #define RS ']'
  101. #define PP '\"'
  102. #define EOL '\0'
  103.  
  104.  
  105. #define TRUE    1
  106. #define FALSE    0
  107. #define ERR        -2
  108. #define FATAL        (ERR-1)
  109. #define CHANGED        (ERR-2)
  110. #define SET_FAIL    (ERR-3)
  111. #define SUB_FAIL    (ERR-4)
  112. #define MEM_FAIL    (ERR-5)
  113.  
  114.  
  115. #define    BUFFER_SIZE    2048    /* stream-buffer size:  == 1 hd cluster */
  116.  
  117. #define LINFREE    1    /* entry not in use */
  118. #define LGLOB    2       /* line marked global */
  119.  
  120. #define MAXLINE    512    /* max number of chars per line */
  121. #define MAXPAT    256    /* max number of chars per replacement pattern */
  122. #define MAXFNAME 256    /* max file name size */
  123.  
  124.  
  125. /**  Global variables  **/
  126.  
  127. extern struct program *current_prog;
  128.  
  129. struct    line {
  130.     int        l_stat;        /* empty, mark */
  131.     struct line    *l_prev;
  132.     struct line    *l_next;
  133.     char        l_buff[1];
  134. };
  135. typedef struct line    LINE;
  136.  
  137. extern struct object *command_giver;
  138. void set_prompt PROT((char *));
  139.  
  140. #ifndef toupper
  141. extern int toupper PROT((int));
  142. #endif
  143.  
  144. int doprnt PROT((int, int));
  145. int ins PROT((char *));
  146. int deflt PROT((int, int));
  147. static int strip_buff PROT((int line,char *buff2));
  148. static void print_help PROT((int arg));
  149. static void print_help2 PROT((void));
  150. static void count_blanks PROT((int line));
  151. static void _count_blanks PROT((char *str, int blanks));
  152.  
  153. #define P_DIAG        (command_giver->interactive->ed_buffer->diag)
  154. #define P_TRUNCFLG    (command_giver->interactive->ed_buffer->truncflg)
  155. #define P_NONASCII    (command_giver->interactive->ed_buffer->nonascii)
  156. #define P_NULLCHAR    (command_giver->interactive->ed_buffer->nullchar)
  157. #define P_TRUNCATED    (command_giver->interactive->ed_buffer->truncated)
  158. #define P_FNAME        (command_giver->interactive->ed_buffer->fname)
  159. #define P_FCHANGED    (command_giver->interactive->ed_buffer->fchanged)
  160. #define P_NOFNAME    (command_giver->interactive->ed_buffer->nofname)
  161. #define P_MARK        (command_giver->interactive->ed_buffer->mark)
  162. #define P_OLDPAT    (command_giver->interactive->ed_buffer->oldpat)
  163. #define P_LINE0        (command_giver->interactive->ed_buffer->Line0)
  164. #define P_LINE0        (command_giver->interactive->ed_buffer->Line0)
  165. #define P_CURLN        (command_giver->interactive->ed_buffer->CurLn)
  166. #define P_CURPTR    (command_giver->interactive->ed_buffer->CurPtr)
  167. #define P_LASTLN    (command_giver->interactive->ed_buffer->LastLn)
  168. #define P_LINE1        (command_giver->interactive->ed_buffer->Line1)
  169. #define P_LINE2        (command_giver->interactive->ed_buffer->Line2)
  170. #define P_NLINES    (command_giver->interactive->ed_buffer->nlines)
  171. #define P_SHIFTWIDTH    (command_giver->interactive->ed_buffer->shiftwidth)
  172. /* shiftwidth is meant to be a 4-bit-value that can be packed into an int
  173.    along with flags, therefore masks 0x1 ... 0x8 are reserved.           */
  174. #define P_FLAGS     (command_giver->interactive->ed_buffer->flags)
  175. #define NFLG_MASK    0x0010
  176. #define P_NFLG        ( P_FLAGS & NFLG_MASK )
  177. #define LFLG_MASK    0x0020
  178. #define P_LFLG        ( P_FLAGS & LFLG_MASK )
  179. #define PFLG_MASK    0x0040
  180. #define P_PFLG        ( P_FLAGS & PFLG_MASK )
  181. #define EIGHTBIT_MASK    0x0080
  182. #define P_EIGHTBIT    ( P_FLAGS & EIGHTBIT_MASK )
  183. #define AUTOINDFLG_MASK    0x0100
  184. #define P_AUTOINDFLG    ( P_FLAGS & AUTOINDFLG_MASK )
  185. #define EXCOMPAT_MASK    0x0200
  186. #define P_EXCOMPAT    ( P_FLAGS & EXCOMPAT_MASK )
  187. #define SHIFTWIDTH_MASK    0x000f
  188. #define ALL_FLAGS_MASK    0x03f0
  189. #define P_APPENDING    (command_giver->interactive->ed_buffer->appending)
  190. #define P_MORE        (command_giver->interactive->ed_buffer->moring)
  191. #define P_LEADBLANKS    (command_giver->interactive->ed_buffer->leading_blanks)
  192. #define P_CUR_AUTOIND   (ED_BUFFER->cur_autoindent)
  193. #define ED_BUFFER       (command_giver->interactive->ed_buffer)
  194.  
  195.  
  196. char    inlin[MAXLINE];
  197. char    *inptr;        /* tty input buffer */
  198. struct ed_buffer {
  199.     int    diag;        /* diagnostic-output? flag */
  200.     int    truncflg;    /* truncate long line flag */
  201.     int    nonascii;    /* count of non-ascii chars read */
  202.     int    nullchar;    /* count of null chars read */
  203.     int    truncated;    /* count of lines truncated */
  204.     char    fname[MAXFNAME];
  205.     int    fchanged;    /* file-changed? flag */
  206.     int    nofname;
  207.     int    mark['z'-'a'+1];
  208.     regexp    *oldpat;
  209.     
  210.     LINE    Line0;
  211.     int    CurLn;
  212.     LINE    *CurPtr;    /* CurLn and CurPtr must be kept in step */
  213.     int    LastLn;
  214.     int    Line1, Line2, nlines;
  215.     int    flags;
  216. #if 0
  217.     int    eightbit;    /* save eighth bit */
  218.     int    nflg;        /* print line number flag */
  219.     int    lflg;        /* print line in verbose mode */
  220.     int    pflg;        /* print current line after each command */
  221. #endif
  222.     int    appending;
  223.     int     moring;         /* used for the wait line of help */
  224.     char    *exit_fn;    /* Function to be called when player exits */
  225.     struct object *exit_ob; /* in this object */
  226.     int    shiftwidth;
  227.     int    leading_blanks;
  228.     int    cur_autoindent;
  229. };
  230.  
  231. struct tbl {
  232.     char    *t_str;
  233.     int    t_and_mask;
  234.     int    t_or_mask;
  235. } *t, tbl[] = {
  236.     "number",    ~FALSE,        NFLG_MASK,
  237.     "nonumber",    ~NFLG_MASK,    FALSE,
  238.     "list",        ~FALSE,        LFLG_MASK,
  239.     "nolist",    ~LFLG_MASK,    FALSE,
  240.     "print",    ~FALSE,     PFLG_MASK,
  241.     "noprint",    ~PFLG_MASK,    FALSE,
  242.     "eightbit",    ~FALSE,        EIGHTBIT_MASK,
  243.     "noeightbit",    ~EIGHTBIT_MASK,    FALSE,
  244.     "autoindent",    ~FALSE,        AUTOINDFLG_MASK,
  245.     "noautoindent",    ~AUTOINDFLG_MASK, FALSE,
  246.     "excompatible", ~FALSE,        EXCOMPAT_MASK,
  247.     "noexcompatible",~EXCOMPAT_MASK,FALSE,
  248.     0
  249. };
  250.  
  251.  
  252. /*-------------------------------------------------------------------------*/
  253.  
  254. #ifndef _AIX
  255. extern    char    *strcpy(), *strncpy();
  256. #endif
  257. extern    char    *xalloc();
  258. extern    LINE    *getptr();
  259. extern    char    *gettxt();
  260. extern    char    *gettxtl();
  261. extern    char    *catsub();
  262. extern    void    prntln(), putcntl(), error();
  263. regexp    *optpat();
  264.  
  265.  
  266. /*________  Macros  ________________________________________________________*/
  267.  
  268. #ifndef max
  269. #  define max(a,b)    ((a) > (b) ? (a) : (b))
  270. #endif
  271.  
  272. #ifndef min
  273. #  define min(a,b)    ((a) < (b) ? (a) : (b))
  274. #endif
  275.  
  276. #define nextln(l)    ((l)+1 > P_LASTLN ? 0 : (l)+1)
  277. #define prevln(l)    ((l)-1 < 0 ? P_LASTLN : (l)-1)
  278.  
  279. #define gettxtl(lin)    ((lin)->l_buff)
  280. #define gettxt(num)    (gettxtl( getptr(num) ))
  281.  
  282. #define getnextptr(p)    ((p)->l_next)
  283. #define getprevptr(p)    ((p)->l_prev)
  284.  
  285. #define setCurLn( lin )    ( P_CURPTR = getptr( P_CURLN = (lin) ) )
  286. #define nextCurLn()    ( P_CURLN = nextln(P_CURLN), P_CURPTR = getnextptr( P_CURPTR ) )
  287. #define prevCurLn()    ( P_CURLN = prevln(P_CURLN), P_CURPTR = getprevptr( P_CURPTR ) )
  288.  
  289. #define clrbuf()    del(1, P_LASTLN)
  290.  
  291. #define    Skip_White_Space    {while (*inptr==SP || *inptr==HT) inptr++;}
  292.  
  293. #define relink(a, x, y, b) { (x)->l_prev = (a); (y)->l_next = (b); }
  294.  
  295.  
  296. /*________  functions  ______________________________________________________*/
  297.  
  298.  
  299. /*    append.c    */
  300.  
  301.  
  302. int append(line, glob)
  303. int    line, glob;
  304. {
  305.     if(glob)
  306.         return(ERR);
  307.     setCurLn( line );
  308.     P_APPENDING = 1;
  309.     if(P_NFLG)
  310.         add_message("%6d. ",P_CURLN+1);
  311.     if (P_CUR_AUTOIND)
  312.         add_message("%*s",P_LEADBLANKS,"");
  313.     set_prompt("*\b");
  314.     return 0;
  315. }
  316.  
  317. int more_append(str)
  318.     char *str;
  319. {
  320.     if(str[0] == '.' && str[1] == '\0') {
  321.         P_APPENDING = 0;
  322.         set_prompt(":");
  323.         return(0);
  324.     }
  325.     if(P_NFLG)
  326.         add_message("%6d. ",P_CURLN+2);
  327.     if ( P_CUR_AUTOIND )
  328.     {
  329.         int i;
  330.         int less_indent_flag = 0;
  331.  
  332.         while ( *str=='\004' || *str == '\013' )
  333.         {
  334.             str++;
  335.             P_LEADBLANKS-=P_SHIFTWIDTH;
  336.             if ( P_LEADBLANKS < 0 ) P_LEADBLANKS=0;
  337.             less_indent_flag=1;
  338.         }
  339.         for ( i=0; i < P_LEADBLANKS; ) inlin[i++]=' ';
  340.         strncpy(inlin+P_LEADBLANKS,str,MAXLINE-P_LEADBLANKS);
  341.         inlin[MAXLINE-1]='\0';
  342.         _count_blanks(inlin,0);
  343.         add_message("%*s",P_LEADBLANKS,"");
  344.         if ( !*str && less_indent_flag ) return 0;
  345.         str=inlin;
  346.     }
  347.     if( ins(str) < 0)
  348.         return( MEM_FAIL );
  349.     return 0;
  350. }
  351.  
  352. static void count_blanks(line)
  353.     int line;
  354. {
  355.     _count_blanks(gettxtl(getptr(line)), 0);
  356. }
  357.  
  358. static void _count_blanks(str,blanks)
  359.     char *str;
  360.     int blanks;
  361. {
  362.     for ( ; *str; str++ )
  363.     {
  364.         if ( *str == ' ' ) blanks++;
  365.         else if ( *str == '\t' ) blanks += 8 - blanks % 8 ;
  366.         else break;
  367.     }
  368.     P_LEADBLANKS = blanks<MAXLINE ? blanks : MAXLINE ;
  369. }
  370.  
  371. /*    ckglob.c    */
  372.  
  373. int ckglob()
  374. {
  375.     regexp    *glbpat;
  376.     char    c, delim, *lin;
  377.     int    num;
  378.     LINE    *ptr;
  379.  
  380.     c = *inptr;
  381.  
  382.     if(c != 'g' && c != 'v')
  383.         return(0);
  384.     if (deflt(1, P_LASTLN) < 0)
  385.         return(ERR);
  386.  
  387.     delim = *++inptr;
  388.     if(delim <= ' ')
  389.         return(ERR);
  390.  
  391.     glbpat = optpat();
  392.     if(*inptr == delim)
  393.         inptr++;
  394.     ptr = getptr(1);
  395.     for (num=1; num<=P_LASTLN; num++) {
  396.         ptr->l_stat &= ~LGLOB;
  397.         if (P_LINE1 <= num && num <= P_LINE2) {
  398.             /* we might have got a NULL pointer if the
  399.                supplied pattern was invalid           */
  400.             if (glbpat) {
  401.                 lin = gettxtl(ptr);
  402.                 if(regexec(glbpat, lin )) {
  403.                     if (c=='g') ptr->l_stat |= LGLOB;
  404.                 } else {
  405.                     if (c=='v') ptr->l_stat |= LGLOB;
  406.                 }
  407.             }
  408.         ptr = getnextptr(ptr);
  409.         }
  410.     }
  411.     return(1);
  412. }
  413.  
  414.  
  415. /*  deflt.c
  416.  *    Set P_LINE1 & P_LINE2 (the command-range delimiters) if the file is
  417.  *    empty; Test whether they have valid values.
  418.  */
  419.  
  420. int deflt(def1, def2)
  421. int    def1, def2;
  422. {
  423.     if(P_NLINES == 0) {
  424.         P_LINE1 = def1;
  425.         P_LINE2 = def2;
  426.     }
  427.     return ( (P_LINE1>P_LINE2 || P_LINE1<=0) ? ERR : 0 );
  428. }
  429.  
  430.  
  431. /*    del.c    */
  432.  
  433. /* One of the calls to this function tests its return value for an error
  434.  * condition.  But del doesn't return any error value, and it isn't obvious
  435.  * to me what errors might be detectable/reportable.  To silence a warning
  436.  * message, I've added a constant return statement. -- RAM
  437.  * ... It could check to<=P_LASTLN ... igp
  438.  */
  439.  
  440. int del(from, to)
  441. int    from, to;
  442. {
  443.     LINE    *first, *last, *next, *tmp;
  444.  
  445.     if(from < 1)
  446.         from = 1;
  447.     first = getprevptr( getptr( from ) );
  448.     last = getnextptr( getptr( to ) );
  449.     next = first->l_next;
  450.     while(next != last && next != &P_LINE0) {
  451.         tmp = next->l_next;
  452.         xfree((char *)next);
  453.         next = tmp;
  454.     }
  455.     relink(first, last, first, last);
  456.     P_LASTLN -= (to - from)+1;
  457.     setCurLn( prevln(from) );
  458.     return(0);
  459. }
  460.  
  461.  
  462. int dolst(line1, line2)
  463. int line1, line2;
  464. {
  465.     int oldflags=P_FLAGS, p;
  466.  
  467.     P_FLAGS |= LFLG_MASK;
  468.     p = doprnt(line1, line2);
  469.     P_FLAGS = oldflags;
  470.     return p;
  471. }
  472.  
  473.  
  474. /*    esc.c
  475.  * Map escape sequences into their equivalent symbols.  Returns the
  476.  * correct ASCII character.  If no escape prefix is present then s
  477.  * is untouched and *s is returned, otherwise **s is advanced to point
  478.  * at the escaped character and the translated character is returned.
  479.  */
  480. int esc(s)
  481. char    **s;
  482. {
  483.     register int    rval;
  484.  
  485.     if (**s != ESCAPE) {
  486.         rval = **s;
  487.     } else {
  488.         (*s)++;
  489.         switch(islower(**s) ? toupper(**s) : **s) {
  490.         case '\000':
  491.             rval = ESCAPE;    break;
  492.         case 'S':
  493.             rval = ' ';    break;
  494.         case 'N':
  495.             rval = '\n';    break;
  496.         case 'T':
  497.             rval = '\t';    break;
  498.         case 'B':
  499.             rval = '\b';    break;
  500.         case 'R':
  501.             rval = '\r';    break;
  502.         default:
  503.             rval = **s;    break;
  504.         }
  505.     }
  506.     return (rval);
  507. }
  508.  
  509.  
  510. /*    doprnt.c    */
  511.  
  512. int doprnt(from, to)
  513. int    from, to;
  514. {
  515.     from = (from < 1) ? 1 : from;
  516.     to = (to > P_LASTLN) ? P_LASTLN : to;
  517.  
  518.     if(to != 0) {
  519.         setCurLn( from );
  520.         while( P_CURLN <= to ) {
  521.             prntln( gettxtl( P_CURPTR ), P_LFLG, (P_NFLG ? P_CURLN : 0));
  522.             if( P_CURLN == to )
  523.                 break;
  524.             nextCurLn();
  525.         }
  526.     }
  527.     return(0);
  528. }
  529.  
  530.  
  531. void prntln(str, vflg, lin)
  532. char    *str;
  533. int    vflg, lin;
  534. {
  535.     char buffer[2048], *p;
  536.  
  537.     if(lin)
  538.         add_message("%7d ",lin);
  539.     p = buffer;
  540.     while(*str && *str != NL) {
  541.         if(*str < ' ' || *str >= 0x7f) {
  542.             switch(*str) {
  543.             case '\t':
  544.                 if(vflg) {
  545.                     putcntl(*str, p);
  546.                     p += 2;
  547.                 } else
  548.                     *p++ = *str;
  549.                 break;
  550.  
  551.             case DEL:
  552.                 *p++ = '^';
  553.                 *p++ = '?';
  554.                 break;
  555.  
  556.             default:
  557.                 putcntl(*str, p);
  558.                 p += 2;
  559.                 break;
  560.             }
  561.         } else
  562.             *p++ = *str;
  563.         str++;
  564.     }
  565.     if(vflg)
  566.         *p++ = '$';
  567.     *p = '\0';
  568.     add_message("%s\n", buffer);
  569. }
  570.  
  571.  
  572. void putcntl(c, buf)
  573. char c, *buf;
  574. {
  575.     buf[0] = '^';
  576.     buf[1] = (c&31)|'@';
  577. }
  578.  
  579. /*    egets.c    */
  580.  
  581. int egets(str,size,stream)
  582. char    *str;
  583. int    size;
  584. FILE    *stream;
  585. {
  586.     int    c, count;
  587.     char    *cp;
  588.  
  589.     for(count = 0, cp = str; size > count;) {
  590.         c = getc(stream);
  591.         if(c == EOF) {
  592.             *cp = EOS;
  593.             if(count)
  594.                 add_message("[Incomplete last line]\n");
  595.             return(count);
  596.         }
  597.         else if(c == NL) {
  598.             *cp = EOS;
  599.             return(++count);
  600.         }
  601.         else if (c == 0)
  602.             P_NULLCHAR++;    /* count nulls */
  603.         else {
  604.             if(c > 127) {
  605.                 if(!P_EIGHTBIT)        /* if not saving eighth bit */
  606.                     c = c&127;    /* strip eigth bit */
  607.                 P_NONASCII++;        /* count it */
  608.             }
  609.             *cp++ = c;    /* not null, keep it */
  610.             count++;
  611.         }
  612.     }
  613.     str[count-1] = EOS;
  614.     if(c != NL) {
  615.         add_message("truncating line\n");
  616.         P_TRUNCATED++;
  617.         while((c = getc(stream)) != EOF)
  618.             if(c == NL)
  619.                 break;
  620.     }
  621.     return(count);
  622. }  /* egets */
  623.  
  624.  
  625. int doread(lin, fname)
  626. int    lin;
  627. char    *fname;
  628. {
  629.     FILE    *fp;
  630.     int    err;
  631.     unsigned long    bytes;
  632.     unsigned int    lines;
  633.     static char    str[MAXLINE];
  634.  
  635.     err = 0;
  636.     P_NONASCII = P_NULLCHAR = P_TRUNCATED = 0;
  637.  
  638.     if (P_DIAG) add_message("\"%s\" ",fname);
  639.     if( (fp = fopen(fname, "r")) == NULL ) {
  640.         add_message(" isn't readable.\n");
  641.         return( ERR );
  642.     }
  643.     setCurLn( lin );
  644.     for(lines = 0, bytes = 0;(err = egets(str,MAXLINE,fp)) > 0;) {
  645.         bytes += err;
  646.         if(ins(str) < 0) {
  647.             err = MEM_FAIL;
  648.             break;
  649.         }
  650.         lines++;
  651.     }
  652.     fclose(fp);
  653.     if(err < 0)
  654.         return(err);
  655.     if (P_DIAG) {
  656.         add_message("%u lines %u bytes",lines,bytes);
  657.         if(P_NONASCII)
  658.             add_message(" [%d non-ascii]",P_NONASCII);
  659.         if(P_NULLCHAR)
  660.             add_message(" [%d nul]",P_NULLCHAR);
  661.         if(P_TRUNCATED)
  662.             add_message(" [%d lines truncated]",P_TRUNCATED);
  663.         add_message("\n");
  664.     }
  665.     return( err );
  666. }  /* doread */
  667.  
  668.  
  669. int dowrite(from, to, fname, apflg)
  670. int    from, to;
  671. char    *fname;
  672. int    apflg;
  673. {
  674.     FILE    *fp;
  675.     int    lin, err;
  676.     unsigned int    lines;
  677.     unsigned long    bytes;
  678.     char    *str;
  679.     LINE    *lptr;
  680.  
  681.     err = 0;
  682.     lines = bytes = 0;
  683.  
  684.     add_message("\"%s\" ",fname);
  685.     if((fp = fopen(fname,(apflg?"a":"w"))) == NULL) {
  686.         add_message(" can't be opened for writing!\n");
  687.         return( ERR );
  688.     }
  689.  
  690.     lptr = getptr(from);
  691.     for(lin = from; lin <= to; lin++) {
  692.         str = lptr->l_buff;
  693.         lines++;
  694.         bytes += strlen(str) + 1;    /* str + '\n' */
  695.         if(fputs(str, fp) == EOF) {
  696.             add_message("file write error\n");
  697.             err++;
  698.             break;
  699.         }
  700.         fputc('\n', fp);
  701.         lptr = lptr->l_next;
  702.     }
  703.     add_message("%u lines %lu bytes\n",lines,bytes);
  704.     fclose(fp);
  705.     return( err );
  706. }  /* dowrite */
  707.  
  708.  
  709. /*    find.c    */
  710.  
  711. int find(pat, dir)
  712. regexp    *pat;
  713. int    dir;
  714. {
  715.     int    i, num;
  716.     LINE    *lin;
  717.  
  718.         dir ? nextCurLn() : prevCurLn() ;
  719.     num = P_CURLN;
  720.     lin = P_CURPTR;
  721.     /* if the typed in pattern was invalid we have a NULL pointer! */
  722.     if (!pat) return ( ERR );
  723.     for(i=0; i<P_LASTLN; i++ ) {
  724.         if(regexec( pat, gettxtl( lin ) ))
  725.             return(num);
  726.         if( dir )
  727.             num = nextln(num), lin = getnextptr(lin);
  728.         else
  729.             num = prevln(num), lin = getprevptr(lin);
  730.     }
  731.     return ( ERR );
  732. }
  733.  
  734. #if 0
  735. /*    findg.c by Ted Gaunt for global searches....    much like 'grep' 
  736.     especially useful when line numbering is turned on.
  737. */
  738. int findg(pat, dir)
  739. regexp    *pat;
  740. int    dir;
  741. {
  742.     int    i, num,count;
  743.     LINE    *lin;
  744.     
  745.     count=0;
  746.     num = P_CURLN;
  747.     lin = P_CURPTR;
  748.     for(i=0; i<P_LASTLN; i++ ) {
  749.     if(regexec( pat, gettxtl( lin ) ))
  750.         {prntln( gettxtl( lin ), P_LFLG, (P_NFLG ? P_CURLN : 0));
  751.          count++;}
  752.     if( dir )
  753.         num = nextln(num), lin = getnextptr(lin);
  754.     else
  755.         num = prevln(num), lin = getprevptr(lin);
  756.     nextCurLn();
  757.     }
  758.     if (!count)
  759.     return ( ERR );
  760.     else return (count);
  761. }
  762. #endif /* 0 */
  763.  
  764. /*    getfn.c    */
  765.  
  766. char *getfn(writeflg)
  767. int writeflg;
  768. {
  769.     static char    file[MAXFNAME];
  770.     char    *cp;
  771.     char *file2;
  772. #ifndef COMPAT_MODE
  773.     struct svalue *ret;
  774. #endif
  775.     
  776.     if(*inptr == NL) {
  777.     P_NOFNAME=TRUE;
  778.     file[0] = '/';
  779.     strcpy(file+1, P_FNAME);
  780.     } else {
  781.     P_NOFNAME=FALSE;
  782.     Skip_White_Space;
  783.     
  784.     cp = file;
  785.     while(*inptr && *inptr != NL && *inptr != SP && *inptr != HT)
  786.         *cp++ = *inptr++;
  787.     *cp = '\0';
  788.     
  789.     }
  790.     if(strlen(file) == 0) {
  791.     add_message("bad file name\n");
  792.     return( NULL );
  793.     }
  794. #ifdef COMPAT_MODE
  795.     file2 = check_file_name(file, writeflg);
  796. #else    
  797.     if (file[0] != '/') {
  798.     push_string(file, STRING_MALLOC);
  799.     ret = apply_master_ob("make_path_absolute", 1);
  800.     if (ret == 0 || ret->type != T_STRING)
  801.         return NULL;
  802.     strncpy(file, ret->u.string, sizeof file - 1);
  803.     }
  804.     file2 = check_valid_path(file, command_giver->eff_user,
  805.                  "ed_start", writeflg);
  806. #endif
  807.     if (!file2)
  808.         return( NULL );
  809.     strncpy(file, file2, MAXFNAME-1);
  810.     file[MAXFNAME-1] = 0;
  811.  
  812.     if(strlen(file) == 0) {
  813.         add_message("no file name\n");
  814.         return(NULL);
  815.     }
  816.     return( file );
  817. }  /* getfn */
  818.  
  819.  
  820. int getnum(first)
  821. int first;
  822. {
  823.     regexp    *srchpat;
  824.     int    num;
  825.     char    c;
  826.  
  827.     Skip_White_Space;
  828.  
  829.     if(*inptr >= '0' && *inptr <= '9') {    /* line number */
  830.         for(num = 0; *inptr >= '0' && *inptr <= '9'; ++inptr) {
  831.             num = (num * 10) + (*inptr - '0');
  832.         }
  833.         return num;
  834.     }
  835.  
  836.     switch(c = *inptr) {
  837.     case '.':
  838.         inptr++;
  839.         return (P_CURLN);
  840.  
  841.     case '$':
  842.         inptr++;
  843.         return (P_LASTLN);
  844.  
  845.     case '/':
  846.     case '?':
  847.         srchpat = optpat();
  848.         if(*inptr == c)
  849.             inptr++;
  850.         return(find(srchpat,c == '/'?1:0));
  851.  
  852. #if 0
  853.       case '^':            /* for grep-like searching */
  854.       case '&':
  855.     srchpat = optpat();
  856.     if(*inptr == c)
  857.         inptr++;
  858.     return(findg(srchpat,c == '^'?1:0));
  859. #endif
  860.     
  861.     case '-':
  862.     case '+':
  863.         return(first ? P_CURLN : 1);
  864.  
  865.     case '\'':
  866.         inptr++;
  867.         if (*inptr < 'a' || *inptr > 'z')
  868.             return(EOF);
  869.         return P_MARK[ *inptr++ - 'a' ];
  870.  
  871.     default:
  872.         return ( first ? EOF : 1 );    /* unknown address */
  873.     }
  874. }  /* getnum */
  875.  
  876.  
  877. /*  getone.c
  878.  *    Parse a number (or arithmetic expression) off the command line.
  879.  */
  880. #define FIRST 1
  881. #define NOTFIRST 0
  882.  
  883. int getone()
  884. {
  885.     int    c, i, num;
  886.  
  887.     if((num = getnum(FIRST)) >= 0) {
  888.         for (;;) {
  889.             Skip_White_Space;
  890.             if(*inptr != '+' && *inptr != '-')
  891.                 break;    /* exit infinite loop */
  892.  
  893.                         c = *inptr++;
  894.             if((i = getnum(NOTFIRST)) < 0)
  895.                 return ( i );
  896.             if(c == '+')
  897.                 num += i;
  898.             else
  899.                 num -= i;
  900.         }
  901.     }
  902.     return ( num>P_LASTLN ? ERR : num );
  903. }  /* getone */
  904.  
  905.  
  906. int getlst()
  907. {
  908.     int    num;
  909.  
  910.     P_LINE2 = 0;
  911.     for(P_NLINES = 0; (num = getone()) >= 0;)
  912.     {
  913.         P_LINE1 = P_LINE2;
  914.         P_LINE2 = num;
  915.         P_NLINES++;
  916.         if(*inptr != ',' && *inptr != ';')
  917.             break;
  918.         if(*inptr == ';')
  919.             setCurLn( num );
  920.         inptr++;
  921.     }
  922.     P_NLINES = min(P_NLINES, 2);
  923.     if(P_NLINES == 0)
  924.         P_LINE2 = P_CURLN;
  925.     if(P_NLINES <= 1)
  926.         P_LINE1 = P_LINE2;
  927.  
  928.     return ( (num == ERR) ? num : P_NLINES );
  929. }  /* getlst */
  930.  
  931.  
  932. /*    getptr.c    */
  933.  
  934. LINE *getptr(num)
  935. int    num;
  936. {
  937.     LINE    *ptr;
  938.     int    j;
  939.  
  940.     if (2*num>P_LASTLN && num<=P_LASTLN) {    /* high line numbers */
  941.         ptr = P_LINE0.l_prev;
  942.         for (j = P_LASTLN; j>num; j--)
  943.             ptr = ptr->l_prev;
  944.     } else {                /* low line numbers */
  945.         ptr = &P_LINE0;
  946.         for(j = 0; j < num; j++)
  947.             ptr = ptr->l_next;
  948.     }
  949.     return(ptr);
  950. }
  951.  
  952.  
  953. /*    getrhs.c    */
  954.  
  955. int getrhs(sub)
  956. char    *sub;
  957. {
  958.     char delim = *inptr++;
  959.     char *outmax = sub + MAXPAT;
  960.     if( delim == NL || *inptr == NL)    /* check for eol */
  961.         return( ERR );
  962.     while( *inptr != delim && *inptr != NL ) {
  963.         if ( sub > outmax )
  964.             return ERR;
  965.         if ( *inptr == ESCAPE ) {
  966.             switch ( *++inptr ) {
  967.             case 'r':
  968.                 *sub++ = '\r';
  969.                 inptr++;
  970.                 break;
  971. #if 0
  972.             case ESCAPE:
  973.                 *sub++ = ESCAPE;
  974.                 *sub++ = ESCAPE;
  975.                 inptr++;
  976. #endif
  977.             case 'n':
  978.                 *sub++ = '\n';
  979.                 inptr++;
  980.                 break;
  981.             case 'b':
  982.                 *sub++ = '\b';
  983.                 inptr++;
  984.                 break;
  985.             case 't':
  986.                 *sub++ = '\t';
  987.                 inptr++;
  988.                 break;
  989.             case '0': {
  990.                 int i=3;
  991.                 *sub = 0;
  992.                 do {
  993.                     if (*++inptr<'0' || *inptr >'7')
  994.                         break;
  995.                     *sub = (*sub<<3) | (*inptr-'0');
  996.                 } while (--i!=0);
  997.                 sub++;
  998.                 } break;
  999. #if 0
  1000.             default:
  1001.                 if ( *inptr != delim )
  1002.                     *sub++ = ESCAPE;
  1003. #else
  1004.             case '&':
  1005.             case '1':
  1006.             case '2':
  1007.             case '3':
  1008.             case '4':
  1009.             case '5':
  1010.             case '6':
  1011.             case '7':
  1012.             case '8':
  1013.             case '9':
  1014.             case '\\':
  1015.                 *sub++ = ESCAPE; /* fall through */
  1016.             default:
  1017. #endif
  1018.                 *sub++ = *inptr;
  1019.                 if ( *inptr != NL )
  1020.                     inptr++;
  1021.             }
  1022.         }
  1023.         else *sub++ = *inptr++;
  1024.     }
  1025.     *sub = '\0';
  1026.  
  1027.     inptr++;        /* skip over delimter */
  1028.     Skip_White_Space;
  1029.     if(*inptr == 'g') {
  1030.         inptr++;
  1031.         return( 1 );
  1032.     }
  1033.     return( 0 );
  1034. }
  1035.  
  1036. /*    ins.c    */
  1037.  
  1038. int ins(str)
  1039. char    *str;
  1040. {
  1041.     char    *cp;
  1042.     LINE    *new, *nxt;
  1043.     int    len;
  1044.  
  1045.     do {
  1046.         for ( cp = str; *cp && *cp != NL; cp++ )
  1047.             ;
  1048.         len = cp - str;
  1049.         /* cp now points to end of first or only line */
  1050.  
  1051.         if((new = (LINE *)xalloc(sizeof(LINE)+len)) == NULL)
  1052.             return( MEM_FAIL );     /* no memory */
  1053.  
  1054.         new->l_stat=0;
  1055.         strncpy(new->l_buff,str,len);    /* build new line */
  1056.         new->l_buff[len] = EOS;
  1057.         nxt = getnextptr(P_CURPTR);    /* get next line */
  1058.         relink(P_CURPTR, new, new, nxt);    /* add to linked list */
  1059.         relink(new, nxt, P_CURPTR, new);
  1060.         P_LASTLN++;
  1061.         P_CURLN++;
  1062.         P_CURPTR = new;
  1063.         str = cp + 1;
  1064.     }
  1065.         while( *cp != EOS );
  1066.     return 1;
  1067. }
  1068.  
  1069.  
  1070. /*    join.c    */
  1071.  
  1072. int join(first, last)
  1073. int first, last;
  1074. {
  1075.     char buf[MAXLINE];
  1076.     char *cp=buf, *str;
  1077.     LINE *lin;
  1078.     int num;
  1079.  
  1080.     if (first<=0 || first>last || last>P_LASTLN)
  1081.         return(ERR);
  1082.     if (first==last) {
  1083.         setCurLn( first );
  1084.         return 0;
  1085.     }
  1086.     lin = getptr(first);
  1087.     for (num=first; num<=last; num++) {
  1088.         str=gettxtl(lin);
  1089.         while ( *str ) {
  1090.             if (cp >= buf + MAXLINE-1 ) {
  1091.                 add_message("line too long\n");
  1092.                 return(ERR);
  1093.             }
  1094.             *cp++ = *str++;
  1095.         }
  1096.         lin = getnextptr(lin);
  1097.     }
  1098.     *cp = EOS;
  1099.     del(first, last);
  1100.     if( ins(buf) < 0 )
  1101.         return MEM_FAIL;
  1102.     P_FCHANGED = TRUE;
  1103.     return 0;
  1104. }
  1105.  
  1106.  
  1107. /*  move.c
  1108.  *    Unlink the block of lines from P_LINE1 to P_LINE2, and relink them
  1109.  *    after line "num".
  1110.  */
  1111.  
  1112. int move(num)
  1113. int    num;
  1114. {
  1115.     int    range;
  1116.     LINE    *before, *first, *last, *after;
  1117.  
  1118.     if( P_LINE1 <= num && num <= P_LINE2 )
  1119.         return( ERR );
  1120.     range = P_LINE2 - P_LINE1 + 1;
  1121.     before = getptr(prevln(P_LINE1));
  1122.     first = getptr(P_LINE1);
  1123.     last = getptr(P_LINE2);
  1124.     after = getptr(nextln(P_LINE2));
  1125.  
  1126.     relink(before, after, before, after);
  1127.     P_LASTLN -= range;    /* per AST's posted patch 2/2/88 */
  1128.     if (num > P_LINE1)
  1129.         num -= range;
  1130.  
  1131.     before = getptr(num);
  1132.     after = getptr(nextln(num));
  1133.     relink(before, first, last, after);
  1134.     relink(last, after, before, first);
  1135.     P_LASTLN += range;    /* per AST's posted patch 2/2/88 */
  1136.     setCurLn( num + range );
  1137.     return( 1 );
  1138. }
  1139.  
  1140.  
  1141. int transfer(num)
  1142. int num;
  1143. {
  1144.     int mid, lin, ntrans;
  1145.  
  1146.     if (P_LINE1<=0 || P_LINE1>P_LINE2)
  1147.         return(ERR);
  1148.  
  1149.     mid= num<P_LINE2 ? num : P_LINE2;
  1150.  
  1151.     setCurLn( num );
  1152.     ntrans=0;
  1153.  
  1154.     for (lin=P_LINE1; lin<=mid; lin++) {
  1155.         if( ins(gettxt(lin)) < 0 )
  1156.             return MEM_FAIL;
  1157.         ntrans++;
  1158.     }
  1159.     lin+=ntrans;
  1160.     P_LINE2+=ntrans;
  1161.  
  1162.     for ( ; lin <= P_LINE2; lin += 2 ) {
  1163.         if( ins(gettxt(lin)) < 0 )
  1164.             return MEM_FAIL;
  1165.         P_LINE2++;
  1166.     }
  1167.     return(1);
  1168. }
  1169.  
  1170.  
  1171. /*    optpat.c    */
  1172.  
  1173. regexp *optpat()
  1174. {
  1175.     char    delim, str[MAXPAT], *cp;
  1176.  
  1177.     delim = *inptr++;
  1178.     if (delim == NL)
  1179.         return P_OLDPAT;
  1180.     cp = str;
  1181.     while(*inptr != delim && *inptr != NL && *inptr != EOS && cp < str + MAXPAT - 1) {
  1182.         if(*inptr == ESCAPE && inptr[1] != NL)
  1183.             *cp++ = *inptr++;
  1184.         *cp++ = *inptr++;
  1185.     }
  1186.  
  1187.     *cp = EOS;
  1188.     if(*str == EOS)
  1189.         return(P_OLDPAT);
  1190.     if(P_OLDPAT)
  1191.         xfree((char *)P_OLDPAT);
  1192.     return P_OLDPAT = regcomp(str,P_EXCOMPAT);
  1193. }
  1194.  
  1195. /* regerror.c */
  1196. void regerror( s )
  1197.     char *s;
  1198. {
  1199.     add_message("ed: %s\n", s );
  1200. }
  1201.  
  1202.  
  1203. int set()
  1204. {
  1205.     char    word[80];
  1206.     int    i;
  1207.  
  1208.     if(*(++inptr) != 't') {
  1209.         if(*inptr != SP && *inptr != HT && *inptr != NL)
  1210.             return(ERR);
  1211.     } else
  1212.         inptr++;
  1213.  
  1214.     if ( (*inptr == NL))
  1215.     {
  1216.         add_message("ed version %d.%d\n", version/100, version%100);
  1217.         for(t = tbl; t->t_str; t+=2) {
  1218.             add_message(    "%s:%s ",t->t_str,
  1219.                 P_FLAGS & t->t_or_mask ?"ON":"OFF");
  1220.         }
  1221.         add_message("\nshiftwidth:%d\n",P_SHIFTWIDTH);
  1222.         return(0);
  1223.     }
  1224.  
  1225.     Skip_White_Space;
  1226.     for(i = 0; *inptr != SP && *inptr != HT && *inptr != NL;) {
  1227.         if (i == sizeof word - 1) {
  1228.         add_message("Too long argument to 'set'!\n");
  1229.         return 0;
  1230.         }
  1231.         word[i++] = *inptr++;
  1232.     }
  1233.     word[i] = EOS;
  1234.     for(t = tbl; t->t_str; t++) {
  1235.         if(strcmp(word,t->t_str) == 0) {
  1236.             P_FLAGS = P_FLAGS & t->t_and_mask | t->t_or_mask;
  1237.             return(0);
  1238.         }
  1239.     }
  1240.     if ( !strcmp(word,"save") ) {
  1241.         struct svalue *ret;
  1242.         push_object(command_giver);
  1243.         push_number( P_SHIFTWIDTH | P_FLAGS );
  1244.         ret = apply_master_ob("save_ed_setup",2);
  1245.         if ( ret && ret->type==T_NUMBER && ret->u.number > 0 )
  1246.             return 0;
  1247.     }
  1248.     if ( !strcmp(word,"shiftwidth") ) {
  1249.         Skip_White_Space;
  1250.         if ( isdigit(*inptr) ) {
  1251.             P_SHIFTWIDTH = *inptr-'0';
  1252.             return 0;
  1253.         }
  1254.     }
  1255.     return SET_FAIL;
  1256. }
  1257.  
  1258. #ifndef relink
  1259. void relink(a, x, y, b)
  1260. LINE    *a, *x, *y, *b;
  1261. {
  1262.     x->l_prev = a;
  1263.     y->l_next = b;
  1264. }
  1265. #endif
  1266.  
  1267.  
  1268. void set_ed_buf()
  1269. {
  1270.     relink(&P_LINE0, &P_LINE0, &P_LINE0, &P_LINE0);
  1271.     P_CURLN = P_LASTLN = 0;
  1272.     P_CURPTR = &P_LINE0;
  1273. }
  1274.  
  1275.  
  1276. /*    subst.c    */
  1277.  
  1278. int subst(pat, sub, gflg, pflag)
  1279. regexp    *pat;
  1280. char    *sub;
  1281. int    gflg, pflag;
  1282. {
  1283.     int    nchngd = 0;
  1284.     char    *txtptr;
  1285.     char    *new, *old, buf[MAXLINE];
  1286.     int    space;            /* amylaar */
  1287.     int    still_running = 1;
  1288.     LINE    *lastline = getptr( P_LINE2 );
  1289.  
  1290.     if(P_LINE1 <= 0)
  1291.         return( SUB_FAIL );
  1292.     nchngd = 0;        /* reset count of lines changed */
  1293.  
  1294.     for( setCurLn( prevln( P_LINE1 ) ); still_running; ) {
  1295.         nextCurLn();
  1296.         new = buf;
  1297.         space = MAXLINE;    /* amylaar */
  1298.         if ( P_CURPTR == lastline )
  1299.             still_running = 0;
  1300.         if ( regexec( pat, txtptr = gettxtl( P_CURPTR ) ) ) {
  1301.             do
  1302.                 {
  1303.                 /* Copy leading text */
  1304.                 int diff = pat->startp[0] - txtptr;
  1305.                 if ( (space-=diff) < 0 )    /* amylaar */
  1306.                     return SUB_FAIL;
  1307.                 strncpy( new, txtptr, diff );
  1308.                 new += diff;
  1309.                 /* Do substitution */
  1310.                 old = new;
  1311.                 new = regsub( pat, sub, new, space);
  1312.                 if (!new || (space-= new-old) < 0) /* amylaar */
  1313.                     return SUB_FAIL;
  1314.                 if (txtptr == pat->endp[0]) { /* amylaar :
  1315.                                        prevent infinite loop */
  1316.                     if ( !*txtptr ) break;
  1317.                     if (--space < 0) return SUB_FAIL;
  1318.                     *new++ = *txtptr++;
  1319.                 } else
  1320.                     txtptr = pat->endp[0];
  1321.                 }
  1322.             while( gflg && !pat->reganch && regexec( pat, txtptr ));
  1323.  
  1324.             /* Copy trailing chars */
  1325.             /* amylaar : always check for enough space left
  1326.              * BEFORE altering memory
  1327.              */
  1328.             if ( (space-= strlen(txtptr)+1 ) < 0 )
  1329.                 return SUB_FAIL;
  1330.             strcpy(new, txtptr);
  1331.             del(P_CURLN,P_CURLN);
  1332.             if( ins(buf) < 0 )
  1333.                 return MEM_FAIL;
  1334.             nchngd++;
  1335.             if(pflag)
  1336.                 doprnt(P_CURLN, P_CURLN);
  1337.         }
  1338.         }
  1339.     return (( nchngd == 0 && !gflg ) ? SUB_FAIL : nchngd);
  1340. }
  1341.  
  1342. /*
  1343.  * Indent code from DGD editor (v0.1), adapted.  No attempt has been made to
  1344.  * optimize for this editor.   Dworkin 920510
  1345.  */
  1346. # define error(s)        { add_message(s); errs++; return; }
  1347. # define bool char
  1348. static int lineno, errs;
  1349. static int shi;        /* the current shift (negative for left shift) */
  1350.  
  1351. /*
  1352.  * NAME:    shift(char*)
  1353.  * ACTION:    Shift a line left or right according to "shi".
  1354.  */
  1355. static void shift(text)
  1356. register char *text;
  1357. {
  1358.     register int index;
  1359.  
  1360.     /* first determine the number of leading spaces */
  1361.     index = 0;
  1362.     while (*text == ' ' || *text == '\t') {
  1363.     if (*text++ == ' ') {
  1364.         index++;
  1365.     } else {
  1366.         index = (index + 8) & ~7;
  1367.     }
  1368.     }
  1369.  
  1370.     if (*text != '\0') { /* don't shift lines with ws only */
  1371.     index += shi;
  1372.     if (index < MAXLINE) {
  1373.         char buffer[MAXLINE];
  1374.         register char *p;
  1375.  
  1376.         p = buffer;
  1377.         /* fill with leading ws */
  1378.         while (index >= 8) {
  1379.         *p++ = '\t';
  1380.         index -= 8;
  1381.         }
  1382.         while (index > 0) {
  1383.         *p++ = ' ';
  1384.         --index;
  1385.         }
  1386.         if (p - buffer + strlen(text) < MAXLINE) {
  1387.         strcpy(p, text);
  1388.         del(lineno, lineno);
  1389.         ins(buffer);
  1390.         return;
  1391.         }
  1392.     }
  1393.  
  1394.     error("Result of shift would be too long\n");
  1395.     }
  1396. }
  1397.  
  1398. # define STACKSZ    1024    /* size of indent stack */
  1399.  
  1400. /* token definitions in indent */
  1401. # define SEMICOLON    0
  1402. # define LBRACKET    1
  1403. # define RBRACKET    2
  1404. # define LOPERATOR    3
  1405. # define ROPERATOR    4
  1406. # define LHOOK        5
  1407. # define RHOOK        6
  1408. # define TOKEN        7
  1409. # define ELSE        8
  1410. # define IF        9
  1411. # define FOR        10
  1412. # define WHILE        11
  1413. # define DO        12
  1414. # define XEOT        13
  1415.  
  1416. static char *stack, *stackbot;    /* token stack */
  1417. static int *ind, *indbot;    /* indent stack */
  1418. static char quote;        /* ' or " */
  1419. static bool in_ppcontrol, in_comment, after_keyword;    /* status */
  1420.  
  1421. /*
  1422.  * NAME:    indent(char*)
  1423.  * ACTION:    Parse and indent a line of text. This isn't perfect, as
  1424.  *        keywords could be defined as macros, comments are very hard to
  1425.  *        handle properly, (, [ and ({ will match any of ), ] and }),
  1426.  *        and last but not least everyone has his own taste of
  1427.  *        indentation.
  1428.  */
  1429. static void indent(buf)
  1430. char *buf;
  1431. {
  1432.     static char f[] = { 7, 1, 7, 1, 2, 1, 6, 4, 2, 6, 7, 7, 2, 0, };
  1433.     static char g[] = { 2, 2, 1, 7, 1, 5, 1, 3, 6, 2, 2, 2, 2, 0, };
  1434.     char text[MAXLINE], ident[MAXLINE];
  1435.     register char *p, *sp;
  1436.     register int *ip;
  1437.     register long index;
  1438.     register int top, token;
  1439.     char *start;
  1440.     bool do_indent;
  1441.  
  1442.     /*
  1443.      * Problem: in this editor memory for deleted lines is reclaimed. So
  1444.      * we cannot shift the line and then continue processing it, as in
  1445.      * DGD ed. Instead make a copy of the line, and process the copy.
  1446.      * Dworkin 920510
  1447.      */
  1448.     strcpy(text, buf);
  1449.  
  1450.     do_indent = FALSE;
  1451.     index = 0;
  1452.     p = text;
  1453.  
  1454.     /* process status vars */
  1455.     if (quote != '\0') {
  1456.     shi = 0;    /* in case a comment starts on this line */
  1457.     } else if (in_ppcontrol || *p == '#') {
  1458.     while (*p != '\0') {
  1459.         if (*p == '\\' && *++p == '\0') {
  1460.         in_ppcontrol = TRUE;
  1461.         return;
  1462.         }
  1463.         p++;
  1464.     }
  1465.     in_ppcontrol = FALSE;
  1466.     return;
  1467.     } else {
  1468.     /* count leading ws */
  1469.     while (*p == ' ' || *p == '\t') {
  1470.         if (*p++ == ' ') {
  1471.         index++;
  1472.         } else {
  1473.         index = (index + 8) & ~7;
  1474.         }
  1475.     }
  1476.     if (*p == '\0') {
  1477.         del(lineno, lineno);
  1478.         ins(p);
  1479.         return;
  1480.     } else if (in_comment) {
  1481.         shift(text);    /* use previous shi */
  1482.     } else {
  1483.         do_indent = TRUE;
  1484.     }
  1485.     }
  1486.  
  1487.     /* process this line */
  1488.     start = p;
  1489.     while (*p != '\0') {
  1490.  
  1491.     /* lexical scanning: find the next token */
  1492.     ident[0] = '\0';
  1493.     if (in_comment) {
  1494.         /* comment */
  1495.         while (*p != '*') {
  1496.         if (*p == '\0') {
  1497.             return;
  1498.         }
  1499.         p++;
  1500.         }
  1501.         while (*p == '*') {
  1502.         p++;
  1503.         }
  1504.         if (*p == '/') {
  1505.         in_comment = FALSE;
  1506.         p++;
  1507.         }
  1508.         continue;
  1509.  
  1510.     } else if (quote != '\0') {
  1511.         /* string or character constant */
  1512.         for (;;) {
  1513.         if (*p == quote) {
  1514.             quote = '\0';
  1515.             p++;
  1516.             break;
  1517.         } else if (*p == '\0') {
  1518.             error("Unterminated string\n");
  1519.         } else if (*p == '\\' && *++p == '\0') {
  1520.             break;
  1521.         }
  1522.         p++;
  1523.         }
  1524.         token = TOKEN;
  1525.  
  1526.     } else {
  1527.         switch (*p++) {
  1528.         case ' ':    /* white space */
  1529.         case '\t':
  1530.         continue;
  1531.  
  1532.         case '\'':    /* start of string */
  1533.         case '"':
  1534.         quote = p[-1];
  1535.         continue;
  1536.  
  1537.         case '/':
  1538.         if (*p == '*') {    /* start of comment */
  1539.             in_comment = TRUE;
  1540.             if (do_indent) {
  1541.             /* this line hasn't been indented yet */
  1542.             shi = *ind - index;
  1543.             shift(text);
  1544.             do_indent = FALSE;
  1545.             } else {
  1546.             register char *q;
  1547.             register int index2;
  1548.  
  1549.             /*
  1550.              * find how much the comment has shifted, so the same
  1551.              * shift can be used if the coment continues on the
  1552.              * next line
  1553.              */
  1554.             index2 = *ind;
  1555.             for (q = start; q < p - 1;) {
  1556.                 if (*q++ == '\t') {
  1557.                 index = (index + 8) & ~7;
  1558.                 index2 = (index2 + 8) & ~7;
  1559.                 } else {
  1560.                 index++;
  1561.                 index2++;
  1562.                 }
  1563.             }
  1564.             shi = index2 - index;
  1565.             }
  1566.             p++;
  1567.             continue;
  1568.         }
  1569.         token = TOKEN;
  1570.         break;
  1571.  
  1572.         case '{':
  1573.         token = LBRACKET;
  1574.         break;
  1575.  
  1576.         case '(':
  1577.         if (after_keyword) {
  1578.             /*
  1579.              * LOPERATOR & ROPERATOR are a kludge. The operator
  1580.              * precedence parser that is used could not work if
  1581.              * parenthesis after keywords was not treated specially.
  1582.              */
  1583.             token = LOPERATOR;
  1584.             break;
  1585.         }
  1586.         if (*p == '{') {
  1587.             p++;    /* ({ is one token */
  1588.         }
  1589.         case '[':
  1590.         token = LHOOK;
  1591.         break;
  1592.  
  1593.         case '}':
  1594.         if (*p != ')') {
  1595.             token = RBRACKET;
  1596.             break;
  1597.         }
  1598.         p++;
  1599.         /* }) is one token; fall through */
  1600.         case ')':
  1601.         case ']':
  1602.         token = RHOOK;
  1603.         break;
  1604.  
  1605.         case ';':
  1606.         token = SEMICOLON;
  1607.         break;
  1608.  
  1609.         default:
  1610.         if (isalpha(*--p) || *p == '_') {
  1611.             register char *q;
  1612.  
  1613.             /* Identifier. See if it's a keyword. */
  1614.             q = ident;
  1615.             do {
  1616.             *q++ = *p++;
  1617.             } while (isalnum(*p) || *p == '_');
  1618.             *q = '\0';
  1619.  
  1620.             if      (strcmp(ident, "if"   ) == 0)    token = IF;
  1621.             else if (strcmp(ident, "else" ) == 0)    token = ELSE;
  1622.             else if (strcmp(ident, "for"  ) == 0)    token = FOR;
  1623.             else if (strcmp(ident, "while") == 0)    token = WHILE;
  1624.             else if (strcmp(ident, "do"   ) == 0)    token = DO;
  1625.             else    /* not a keyword */            token = TOKEN;
  1626.         } else {
  1627.             /* anything else is a "token" */
  1628.             p++;
  1629.             token = TOKEN;
  1630.         }
  1631.         break;
  1632.         }
  1633.     }
  1634.  
  1635.     /* parse */
  1636.     sp = stack;
  1637.     ip = ind;
  1638.     for (;;) {
  1639.         top = *sp;
  1640.         if (top == LOPERATOR && token == RHOOK) {
  1641.         /* ) after LOPERATOR is ROPERATOR */
  1642.         token = ROPERATOR;
  1643.         }
  1644.  
  1645.         if (f[top] <= g[token]) {    /* shift the token on the stack */
  1646.         register int i;
  1647.  
  1648.         if (sp == stackbot) {
  1649.             /* out of stack */
  1650.             error("Nesting too deep\n");
  1651.         }
  1652.  
  1653.         /* handle indentation */
  1654.         i = *ip;
  1655.         /* if needed, reduce indentation prior to shift */
  1656.         if ((token == LBRACKET && 
  1657.           (*sp == ROPERATOR || *sp == ELSE || *sp == DO)) ||
  1658.           token == RBRACKET ||
  1659.           (token == IF && *sp == ELSE)) {
  1660.             /* back up */
  1661.             i -= P_SHIFTWIDTH;
  1662.         }
  1663.         /* shift the current line, if appropriate */
  1664.         if (do_indent) {
  1665.             shi = i - index;
  1666.             if (i > 0 && token != RHOOK &&
  1667.               (*sp == LOPERATOR || *sp == LHOOK)) {
  1668.             /* half indent after ( [ ({ (HACK!) */
  1669.             shi += P_SHIFTWIDTH / 2;
  1670.             } else if (token == TOKEN && *sp == LBRACKET &&
  1671.               (strcmp(ident, "case") == 0 ||
  1672.               strcmp(ident, "default") == 0)) {
  1673.             /* back up if this is a switch label */
  1674.             shi -= P_SHIFTWIDTH;
  1675.             }
  1676.             shift(text);
  1677.             do_indent = FALSE;
  1678.         }
  1679.         /* change indentation after current token */
  1680.         if (token == LBRACKET || token == ROPERATOR || token == ELSE ||
  1681.           token == DO) {
  1682.             /* add indentation */
  1683.             i += P_SHIFTWIDTH;
  1684.         } else if (token == SEMICOLON &&
  1685.           (*sp == ROPERATOR || *sp == ELSE)) {
  1686.             /* in case it is followed by a comment */
  1687.             i -= P_SHIFTWIDTH;
  1688.         }
  1689.  
  1690.         *--sp = token;
  1691.         *--ip = i;
  1692.         break;
  1693.         }
  1694.  
  1695.         /* reduce handle */
  1696.         do {
  1697.         top = *sp++;
  1698.         ip++;
  1699.         } while (f[*sp] >= g[top]);
  1700.     }
  1701.     stack = sp;
  1702.     ind = ip;
  1703.     after_keyword = (token >= IF);    /* but not after ELSE */
  1704.     }
  1705. }
  1706.  
  1707. static int indent_code()
  1708. {
  1709.     char s[STACKSZ];
  1710.     int i[STACKSZ];
  1711.     int last;
  1712.  
  1713.     /* setup stacks */
  1714.     stackbot = s;
  1715.     indbot = i;
  1716.     stack = stackbot + STACKSZ - 1;
  1717.     *stack = XEOT;
  1718.     ind = indbot + STACKSZ - 1;
  1719.     *ind = 0;
  1720.  
  1721.     quote = '\0';
  1722.     in_ppcontrol = FALSE;
  1723.     in_comment = FALSE;
  1724.  
  1725.     P_FCHANGED = TRUE;
  1726.     last = P_LASTLN;
  1727.     errs = 0;
  1728.  
  1729.     for (lineno = 1; lineno <= last; lineno++) {
  1730.     setCurLn(lineno);
  1731.     indent(gettxtl(P_CURPTR));
  1732.     if (errs != 0) {
  1733.         return ERR;
  1734.     }
  1735.     }
  1736.  
  1737.     return 0;
  1738. }
  1739.  
  1740. # undef bool
  1741. # undef error
  1742. /* end of indent code */
  1743.  
  1744. /*  docmd.c
  1745.  *    Perform the command specified in the input buffer, as pointed to
  1746.  *    by inptr.  Actually, this finds the command letter first.
  1747.  */
  1748.  
  1749. int docmd(glob)
  1750. int    glob;
  1751. {
  1752.     static char    rhs[MAXPAT];
  1753.     regexp    *subpat;
  1754.     int    c, err, line3;
  1755.     int    apflg, pflag, gflag;
  1756.     int    nchng;
  1757.     char    *fptr;
  1758.  
  1759.     pflag = FALSE;
  1760.     Skip_White_Space;
  1761.  
  1762.     c = *inptr++;
  1763.     switch(c) {
  1764.     case NL:
  1765.         if( P_NLINES == 0 && (P_LINE2 = nextln(P_CURLN)) == 0 )
  1766.             return(ERR);
  1767.         setCurLn( P_LINE2 );
  1768.         return (1);
  1769.  
  1770.     case '=':
  1771.         add_message("%d\n",P_LINE2);
  1772.         break;
  1773.  
  1774.     case 'a':
  1775.     case 'A':
  1776.         P_CUR_AUTOIND = c=='a' ? P_AUTOINDFLG : !P_AUTOINDFLG;
  1777.         if(*inptr != NL || P_NLINES > 1)
  1778.             return(ERR);
  1779.  
  1780.         if ( P_CUR_AUTOIND ) count_blanks(P_LINE1);
  1781.         if(append(P_LINE1, glob) < 0)
  1782.             return(ERR);
  1783.         P_FCHANGED = TRUE;
  1784.         break;
  1785.  
  1786.     case 'c':
  1787.         if(*inptr != NL)
  1788.             return(ERR);
  1789.  
  1790.         if(deflt(P_CURLN, P_CURLN) < 0)
  1791.             return(ERR);
  1792.  
  1793.         P_CUR_AUTOIND = P_AUTOINDFLG;
  1794.         if ( P_AUTOINDFLG ) count_blanks(P_LINE1);
  1795.         if(del(P_LINE1, P_LINE2) < 0)
  1796.             return(ERR);
  1797.         if(append(P_CURLN, glob) < 0)
  1798.             return(ERR);
  1799.         P_FCHANGED = TRUE;
  1800.         break;
  1801.  
  1802.     case 'd':
  1803.         if(*inptr != NL)
  1804.             return(ERR);
  1805.  
  1806.         if(deflt(P_CURLN, P_CURLN) < 0)
  1807.             return(ERR);
  1808.  
  1809.         if(del(P_LINE1, P_LINE2) < 0)
  1810.             return(ERR);
  1811.         if(nextln(P_CURLN) != 0)
  1812.             nextCurLn();
  1813.         P_FCHANGED = TRUE;
  1814.         break;
  1815.  
  1816.     case 'e':
  1817.         if(P_NLINES > 0)
  1818.             return(ERR);
  1819.         if(P_FCHANGED)
  1820.             return CHANGED;
  1821.         /*FALL THROUGH*/
  1822.     case 'E':
  1823.         if(P_NLINES > 0)
  1824.             return(ERR);
  1825.  
  1826.         if(*inptr != ' ' && *inptr != HT && *inptr != NL)
  1827.             return(ERR);
  1828.  
  1829.         if((fptr = getfn(0)) == NULL)
  1830.             return(ERR);
  1831.  
  1832.         clrbuf();
  1833.         (void)doread(0, fptr);
  1834.  
  1835.         strcpy(P_FNAME, fptr);
  1836.         P_FCHANGED = FALSE;
  1837.         break;
  1838.  
  1839.     case 'f':
  1840.         if(P_NLINES > 0)
  1841.             return(ERR);
  1842.  
  1843.         if(*inptr != ' ' && *inptr != HT && *inptr != NL)
  1844.             return(ERR);
  1845.  
  1846.         fptr = getfn(0);
  1847.  
  1848.         if (P_NOFNAME)
  1849.             add_message("%s\n", P_FNAME);
  1850.         else {
  1851.             if(fptr == NULL) return(ERR);
  1852.             strcpy(P_FNAME, fptr);
  1853.         }
  1854.         break;
  1855.  
  1856.     case 'i':
  1857.         if(*inptr != NL || P_NLINES > 1)
  1858.             return(ERR);
  1859.  
  1860.         P_CUR_AUTOIND = P_AUTOINDFLG;
  1861.         if ( P_AUTOINDFLG ) count_blanks(P_LINE1);
  1862.         if(append(prevln(P_LINE1), glob) < 0)
  1863.             return(ERR);
  1864.         P_FCHANGED = TRUE;
  1865.         break;
  1866.  
  1867.     case 'j':
  1868.         if (*inptr != NL || deflt(P_CURLN, P_CURLN+1)<0)
  1869.             return(ERR);
  1870.  
  1871.         if (join(P_LINE1, P_LINE2) < 0)
  1872.             return(ERR);
  1873.         break;
  1874.  
  1875.     case 'k':
  1876.         Skip_White_Space;
  1877.  
  1878.         if (*inptr < 'a' || *inptr > 'z')
  1879.             return ERR;
  1880.         c= *inptr++;
  1881.  
  1882.         if(*inptr != ' ' && *inptr != HT && *inptr != NL)
  1883.             return(ERR);
  1884.  
  1885.         P_MARK[c-'a'] = P_LINE1;
  1886.         break;
  1887.  
  1888.     case 'l':
  1889.         if(*inptr != NL)
  1890.             return(ERR);
  1891.         if(deflt(P_CURLN,P_CURLN) < 0)
  1892.             return(ERR);
  1893.         if (dolst(P_LINE1,P_LINE2) < 0)
  1894.             return(ERR);
  1895.         break;
  1896.  
  1897.     case 'm':
  1898.         if((line3 = getone()) < 0)
  1899.             return(ERR);
  1900.         if(deflt(P_CURLN,P_CURLN) < 0)
  1901.             return(ERR);
  1902.         if(move(line3) < 0)
  1903.             return(ERR);
  1904.         P_FCHANGED = TRUE;
  1905.         break;
  1906.  
  1907.       case 'n':
  1908.     P_FLAGS = P_FLAGS & ~NFLG_MASK & ~LFLG_MASK
  1909.         | ( NFLG_MASK | LFLG_MASK ) & !P_NFLG ;
  1910.     P_DIAG=!P_DIAG;
  1911.     add_message(    "number %s, list %s\n",
  1912.             P_NFLG?"ON":"OFF",
  1913.             P_LFLG?"ON":"OFF");
  1914.     break;
  1915.         
  1916.       case 'I':
  1917.     if(P_NLINES > 0)
  1918.         return(ERR);
  1919.     if(*inptr != NL)
  1920.         return(ERR);
  1921.     add_message("Indenting entire code...\n");
  1922.     if (indent_code())
  1923.         add_message("Indention halted.\n");
  1924.     else 
  1925.         add_message("Done indenting.\n");
  1926.     break;
  1927.  
  1928.       case 'H':
  1929.       case 'h': 
  1930.     print_help(*(inptr++));
  1931.     break;
  1932.  
  1933.     case 'P':
  1934.     case 'p':
  1935.         if(*inptr != NL)
  1936.             return(ERR);
  1937.         if(deflt(P_CURLN,P_CURLN) < 0)
  1938.             return(ERR);
  1939.         if(doprnt(P_LINE1,P_LINE2) < 0)
  1940.             return(ERR);
  1941.         break;
  1942.  
  1943.     case 'q':
  1944.         if(P_FCHANGED)
  1945.             return CHANGED;
  1946.         /*FALL THROUGH*/
  1947.     case 'Q':
  1948.         clrbuf();
  1949.         if(*inptr == NL && P_NLINES == 0 && !glob)
  1950.             return(EOF);
  1951.         else
  1952.             return(ERR);
  1953.  
  1954.     case 'r':
  1955.         if(P_NLINES > 1)
  1956.             return(ERR);
  1957.  
  1958.         if(P_NLINES == 0)        /* The original code tested */
  1959.             P_LINE2 = P_LASTLN;    /*    if(P_NLINES = 0)    */
  1960.                         /* which looks wrong.  RAM  */
  1961.  
  1962.         if(*inptr != ' ' && *inptr != HT && *inptr != NL)
  1963.             return(ERR);
  1964.  
  1965.         if((fptr = getfn(0)) == NULL)
  1966.             return(ERR);
  1967.  
  1968.         if((err = doread(P_LINE2, fptr)) < 0)
  1969.             return(err);
  1970.         P_FCHANGED = TRUE;
  1971.         break;
  1972.  
  1973.     case 's':
  1974.         if(*inptr == 'e')
  1975.             return(set());
  1976.         Skip_White_Space;
  1977.         if((subpat = optpat()) == NULL)
  1978.             return(ERR);
  1979.         if((gflag = getrhs(rhs)) < 0)
  1980.             return(ERR);
  1981.         if(*inptr == 'p')
  1982.             pflag++;
  1983.         if(deflt(P_CURLN, P_CURLN) < 0)
  1984.             return(ERR);
  1985.         if((nchng = subst(subpat, rhs, gflag, pflag)) < 0)
  1986.             return(ERR);
  1987.         if(nchng)
  1988.             P_FCHANGED = TRUE;
  1989.         if ( nchng==1 && P_PFLG ) {
  1990.             if(doprnt(P_CURLN, P_CURLN) < 0)
  1991.             return(ERR);
  1992.         }
  1993.         break;
  1994.  
  1995.     case 't':
  1996.         if((line3 = getone()) < 0)
  1997.             return(ERR);
  1998.         if(deflt(P_CURLN,P_CURLN) < 0)
  1999.             return(ERR);
  2000.         if(transfer(line3) < 0)
  2001.             return(ERR);
  2002.         P_FCHANGED = TRUE;
  2003.         break;
  2004.  
  2005.     case 'W':
  2006.     case 'w':
  2007.         apflg = (c=='W');
  2008.  
  2009.         if(*inptr != ' ' && *inptr != HT && *inptr != NL)
  2010.             return(ERR);
  2011.  
  2012.         if((fptr = getfn(1)) == NULL)
  2013.             return(ERR);
  2014.  
  2015.         if(deflt(1, P_LASTLN) < 0)
  2016.             return(ERR);
  2017.         if(dowrite(P_LINE1, P_LINE2, fptr, apflg) < 0)
  2018.             return(ERR);
  2019.         P_FCHANGED = FALSE;
  2020.         break;
  2021.  
  2022.     case 'x':
  2023.         if(*inptr == NL && P_NLINES == 0 && !glob) {
  2024.             if((fptr = getfn(1)) == NULL)
  2025.                 return(ERR);
  2026.             if(dowrite(1, P_LASTLN, fptr, 0) >= 0)
  2027.                 return(EOF);
  2028.         }
  2029.         return(ERR);
  2030.  
  2031.     case 'z':
  2032.         if(deflt(P_CURLN,P_CURLN) < 0)
  2033.             return(ERR);
  2034.  
  2035.         switch(*inptr) {
  2036.         case '-':
  2037.             if(doprnt(P_LINE1-21,P_LINE1) < 0)
  2038.                 return(ERR);
  2039.             break;
  2040.  
  2041.         case '.':
  2042.             if(doprnt(P_LINE1-11,P_LINE1+10) < 0)
  2043.                 return(ERR);
  2044.             break;
  2045.  
  2046.         case '+':
  2047.         case '\n':
  2048.             if(doprnt(P_LINE1,P_LINE1+21) < 0)
  2049.                 return(ERR);
  2050.             break;
  2051.         }
  2052.         break;
  2053.  
  2054.       case 'Z':
  2055.     if(deflt(P_CURLN,P_CURLN) < 0)
  2056.         return(ERR);
  2057.     
  2058.     switch(*inptr) {
  2059.       case '-':
  2060.         if(doprnt(P_LINE1-41,P_LINE1) < 0)
  2061.         return(ERR);
  2062.         break;
  2063.         
  2064.       case '.':
  2065.         if(doprnt(P_LINE1-21,P_LINE1+20) < 0)
  2066.         return(ERR);
  2067.         break;
  2068.         
  2069.       case '+':
  2070.       case '\n':
  2071.         if(doprnt(P_LINE1,P_LINE1+41) < 0)
  2072.         return(ERR);
  2073.         break;
  2074.     }
  2075.     break;    
  2076.     default:
  2077.         return(ERR);
  2078.     }
  2079.  
  2080.     return (0);
  2081. }  /* docmd */
  2082.  
  2083.  
  2084. /*    doglob.c    */
  2085. int doglob()
  2086. {
  2087.     int    lin, status;
  2088.     char    *cmd;
  2089.     LINE    *ptr;
  2090.  
  2091.     cmd = inptr;
  2092.  
  2093.     for (;;) {
  2094.         ptr = getptr(1);
  2095.         for (lin=1; lin<=P_LASTLN; lin++) {
  2096.             if (ptr->l_stat & LGLOB)
  2097.                 break;
  2098.             ptr = getnextptr(ptr);
  2099.         }
  2100.         if (lin > P_LASTLN)
  2101.             break;
  2102.  
  2103.         ptr->l_stat &= ~LGLOB;
  2104.         P_CURLN = lin; P_CURPTR = ptr;
  2105.         inptr = cmd;
  2106.         if((status = getlst()) < 0)
  2107.             return(status);
  2108.         if((status = docmd(1)) < 0)
  2109.             return(status);
  2110.     }
  2111.     return(P_CURLN);
  2112. }  /* doglob */
  2113.  
  2114.  
  2115. /*
  2116.  * Start the editor. Because several players can edit simultaneously,
  2117.  * they will each need a separate editor data block.
  2118.  *
  2119.  * If an exit_fn and exit_ob is given, then call exit_ob->exit_fn at
  2120.  * exit of editor. The purpose is to make it possible for external LPC
  2121.  * code to maintain a list of locked files.
  2122.  */
  2123. void ed_start(file_arg, exit_fn, exit_ob)
  2124.     char *file_arg;
  2125.     char *exit_fn;
  2126.     struct object *exit_ob;
  2127. {
  2128.     struct svalue *setup;
  2129.  
  2130.     if (!command_giver->interactive)
  2131.         error("Tried to start an ed session on a non-interative player.\n");
  2132.     if (ED_BUFFER)
  2133.         error("Tried to start an ed session, when already active.\n");
  2134. #ifdef COMPAT_MODE
  2135.     if (command_giver != current_object)
  2136.         error("Illegal start of ed.\n");
  2137. #endif
  2138.     ED_BUFFER = (struct ed_buffer *)xalloc(sizeof (struct ed_buffer));
  2139.     memset((char *)command_giver->interactive->ed_buffer, '\0',
  2140.            sizeof (struct ed_buffer));
  2141.     ED_BUFFER->truncflg = 1;
  2142.     ED_BUFFER->flags |= EIGHTBIT_MASK;
  2143.     ED_BUFFER->shiftwidth= 4;
  2144.     push_object(command_giver);
  2145.     setup = apply_master_ob("retrieve_ed_setup",1);
  2146.     if ( setup && setup->type==T_NUMBER && setup->u.number ) {
  2147.         ED_BUFFER->flags      = setup->u.number & ALL_FLAGS_MASK;
  2148.         ED_BUFFER->shiftwidth = setup->u.number & SHIFTWIDTH_MASK;
  2149.     }
  2150.     ED_BUFFER->CurPtr =
  2151.         &command_giver->interactive->ed_buffer->Line0;
  2152.     if (exit_fn) {
  2153.         ED_BUFFER->exit_fn = string_copy(exit_fn);
  2154.         exit_ob->ref ++ ;
  2155.     } else {
  2156.         ED_BUFFER->exit_fn = 0;
  2157.     }
  2158.     ED_BUFFER->exit_ob = exit_ob;
  2159.     set_ed_buf();
  2160.  
  2161.     /*
  2162.      * Check for read on startup, since the buffer is read in. But don't
  2163.      * check for write, since we may want to change the file name.
  2164.      * When in compatibility mode, we assume that the test of valid read
  2165.      * is done by the caller of ed().
  2166.      */
  2167.     if(file_arg
  2168. #ifndef COMPAT_MODE
  2169.        && (file_arg =
  2170.            check_valid_path(file_arg, command_giver->eff_user,
  2171.                 "ed_start", 0))
  2172. #endif       
  2173.        && !doread(0, file_arg)) {
  2174.         setCurLn( 1 );
  2175.     }
  2176.     if (file_arg) {
  2177.         strncpy(P_FNAME, file_arg, MAXFNAME-1);
  2178.         P_FNAME[MAXFNAME-1] = 0;
  2179.         add_message("/%s, %d lines\n", file_arg, P_LASTLN);
  2180.     } else {
  2181.         add_message("No file.\n");
  2182.     }
  2183. #if defined(NLHACK) && defined(COMPAT_MODE)
  2184.     push_number(1);
  2185.     (void) apply("set_busy_ed", command_giver,1);
  2186. #endif
  2187.     set_prompt(":");
  2188.     return;
  2189. }
  2190.  
  2191. static void free_ed_buffer() {
  2192.     clrbuf();
  2193.     if (ED_BUFFER->exit_fn) {
  2194.     char *name;
  2195.     struct object *ob;
  2196.         ob = ED_BUFFER->exit_ob;
  2197.     name = ED_BUFFER->exit_fn;
  2198.         xfree((char *)ED_BUFFER);
  2199.     ED_BUFFER = 0;
  2200.     set_prompt("> ");
  2201.         apply(name,
  2202.             ob, 0);
  2203.         xfree(name);
  2204.         free_object(ob, "ed EOF");
  2205. #if defined(NLHACK) && defined(COMPAT_MODE)
  2206.     push_number(0);
  2207.     (void) apply("set_busy_ed", command_giver, 1);
  2208. #endif
  2209.         return;
  2210.     }
  2211.     xfree((char *)ED_BUFFER);
  2212.     ED_BUFFER = 0;
  2213. #if defined(NLHACK) && defined(COMPAT_MODE)
  2214.     push_number(0);
  2215.     (void) apply("set_busy_ed", command_giver, 1);
  2216. #endif
  2217.     add_message("Exit from ed.\n");
  2218.     set_prompt("> ");
  2219.     return;
  2220. }
  2221.  
  2222. void ed_cmd(str)
  2223.     char *str;
  2224. {
  2225.     int status;
  2226.  
  2227.     if (P_MORE) {
  2228.         print_help2();
  2229.         return;
  2230.     }
  2231.     if (P_APPENDING) {
  2232.         more_append(str);
  2233.         return;
  2234.     }
  2235.     if (strlen(str) < MAXLINE)
  2236.         strcat(str, "\n");
  2237.     
  2238.     strncpy(inlin, str, MAXLINE-1);
  2239.     inlin[MAXLINE-1] = 0;
  2240.     inptr = inlin;
  2241.     if(getlst() >= 0)
  2242.         if((status = ckglob()) != 0) {
  2243.             if(status >= 0 && (status = doglob()) >= 0) {
  2244.                 setCurLn( status );
  2245.                 return;
  2246.             }
  2247.         } else {
  2248.             if((status = docmd(0)) >= 0) {
  2249.                 if(status == 1)
  2250.                     doprnt(P_CURLN, P_CURLN);
  2251.                 return;
  2252.             }
  2253.         }
  2254.     switch (status) {
  2255.     case EOF:
  2256.             free_ed_buffer();
  2257.         return;
  2258.     case FATAL:
  2259.         if (ED_BUFFER->exit_fn) {
  2260.             xfree(ED_BUFFER->exit_fn);
  2261.             free_object(ED_BUFFER->exit_ob, "ed FATAL");
  2262.         }
  2263.         xfree((char *)ED_BUFFER);
  2264.         ED_BUFFER= 0;
  2265.         add_message("FATAL ERROR\n");
  2266. #if defined(NLHACK) && defined(COMPAT_MODE)
  2267.         push_number(0);
  2268.         (void) apply("set_busy_ed", command_giver, 1);
  2269. #endif
  2270.         set_prompt("> ");
  2271.         return;
  2272.     case CHANGED:
  2273.         add_message("File has been changed.\n");
  2274.         break;
  2275.     case SET_FAIL:
  2276.         add_message("`set' command failed.\n");
  2277.         break;
  2278.     case SUB_FAIL:
  2279.         add_message("string substitution failed.\n");
  2280.         break;
  2281.     case MEM_FAIL:
  2282.         add_message("Out of memory: text may have been lost.\n" );
  2283.         break;
  2284.     default:
  2285.         add_message("Unrecognized or failed command.\n");
  2286.         /*  Unrecognized or failed command (this  */
  2287.         /*  is SOOOO much better than "?" :-)      */
  2288.     }
  2289. }
  2290.  
  2291. void save_ed_buffer()
  2292. {
  2293.     struct svalue *stmp;
  2294.     char *fname;
  2295.  
  2296.     push_string(P_FNAME, STRING_SHARED);
  2297.     stmp = apply_master_ob("get_ed_buffer_save_file_name",1);
  2298.     if (stmp) {
  2299.     if (stmp->type == T_STRING) {
  2300.         fname = stmp->u.string;
  2301.         if (*fname == '/') fname++;
  2302.             dowrite(1, P_LASTLN, fname , 0);
  2303.     }
  2304.         free_svalue(stmp);
  2305.     }
  2306.     free_ed_buffer();
  2307. }
  2308.  
  2309. static void print_help(arg)
  2310.     int arg;
  2311. {
  2312.     switch (arg) {
  2313.     case 'I':
  2314.     add_message("This command indents your entire file under the\n");
  2315.     add_message("assumption that it is LPC code.  It is only useful\n");
  2316.     add_message("for files that are not yet indented, since the\n");
  2317.     add_message("indentation style is unlikely to satisfy anyone.\n");
  2318.     add_message("Originally from DGD ed.\n");
  2319.     break;
  2320. #if 0
  2321.     case '^':
  2322.     add_message("Command: ^   Usage: ^pattern\n");
  2323.     add_message("This command is similiar to grep, in that it searches the\n");
  2324.     add_message("entire file, printing every line that contains the specified\n");
  2325.     add_message("pattern.  To get the line numbers of found lines, turn on line\n");
  2326.     add_message("number printing with the 'n' command.\n");
  2327.     break;
  2328. #endif
  2329.     case 'n':
  2330.     add_message("Command: n   Usage: n\n");
  2331.     add_message("This command toggles the internal flag which will cause line\n");
  2332.     add_message("numbers to be printed whenever a line is listed.\n");
  2333.     break;
  2334.     case 'a':
  2335.     add_message("Command: a   Usage: a\n");
  2336.     add_message("Append causes the editor to enter input mode, inserting all text\n");
  2337.     add_message("starting AFTER the current line. Use a '.' on a blank line to exit\n");
  2338.     add_message("this mode.\n");
  2339.     break;
  2340.     case 'A':
  2341.         add_message("Command: A   Usage: A\n\
  2342. Like the 'a' command, but uses inverse autoindent mode.\n");
  2343.     break;
  2344.     case 'i':
  2345.     add_message("Command: i   Usage: i\n");
  2346.     add_message("Insert causes the editor to enter input mode, inserting all text\n");
  2347.     add_message("starting BEFORE the current line. Use a '.' on a blank line to exit\n");
  2348.     add_message("this mode.\n");
  2349.     break;
  2350.     case 'c':
  2351.     add_message("Command: c   Usage: c\n");
  2352.     add_message("Change command causes the current line to be wiped from memory.\n");
  2353.     add_message("The editor enters input mode and all text is inserted where the previous\n");
  2354.     add_message("line existed.\n");
  2355.     break;
  2356.     case 'd':
  2357.     add_message("Command: d   Usage: d  or [range]d\n");
  2358.     add_message("Deletes the current line unless preceeded with a range of lines,\n");
  2359.     add_message("then the entire range will be deleted.\n");
  2360.     break;
  2361.     case 'e':
  2362.     add_message("Commmand: e  Usage: e filename\n");
  2363.     add_message("Causes the current file to be wiped from memory, and the new file\n");
  2364.     add_message("to be loaded in.\n");
  2365.     break;      
  2366.     case 'E':
  2367.     add_message("Commmand: E  Usage: E filename\n");
  2368.     add_message("Causes the current file to be wiped from memory, and the new file\n");
  2369.     add_message("to be loaded in.  Different from 'e' in the fact that it will wipe\n");
  2370.     add_message("the current file even if there are unsaved modifications.\n");
  2371.     break;
  2372.     case 'f':
  2373.     add_message("Command: f  Usage: f  or f filename\n");
  2374.     add_message("Display or set the current filename.   If  filename is given as \nan argument, the file (f) command changes the current filename to\nfilename; otherwise, it prints  the current filename.\n");
  2375.     break;
  2376.     case 'g':
  2377.     add_message("Command: g  Usage: g/re/p\n");
  2378.     add_message("Search in all lines for expression 're', and print\n");
  2379.     add_message("every match. Command 'l' can also be given\n");
  2380.     add_message("Unlike in unix ed, you can also supply a range of lines\n");
  2381.     add_message("to search in\n");
  2382.     add_message("Compare with command 'v'.\n");
  2383.     break;
  2384.     case 'h':
  2385.     add_message("Command: h    Usage:  h  or hc (where c is a command)\n");
  2386.     add_message("Help files added by Qixx.\n");
  2387.     break;
  2388.     case 'j':
  2389.     add_message("Command: j    Usage: j or [range]j\n");
  2390.     add_message("Join Lines. Remove the NEWLINE character  from  between the  two\naddressed lines.  The defaults are the current line and the line\nfollowing.  If exactly one address is given,  this  command does\nnothing.  The joined line is the resulting current line.\n");
  2391.     break;
  2392.     case 'k':
  2393.     add_message("Command: k   Usage: kc  (where c is a character)\n");
  2394.     add_message("Mark the addressed line with the name c,  a  lower-case\nletter.   The  address-form,  'c,  addresses  the  line\nmarked by c.  k accepts one address; the default is the\ncurrent line.  The current line is left unchanged.\n");
  2395.     break;
  2396.     case 'l':
  2397.     add_message("Command: l   Usage: l  or  [range]l\n");
  2398.     add_message("List the current line or a range of lines in an unambiguous\nway such that non-printing characters are represented as\nsymbols (specifically New-Lines).\n");
  2399.     break;
  2400.     case 'm':
  2401.     add_message("Command: m   Usage: mADDRESS or [range]mADDRESS\n");
  2402.     add_message("Move the current line (or range of lines if specified) to a\nlocation just after the specified ADDRESS.  Address 0 is the\nbeginning of the file and the default destination is the\ncurrent line.\n");
  2403.     break;
  2404.     case 'p':
  2405.     add_message("Command: p    Usage: p  or  [range]p\n");
  2406.     add_message("Print the current line (or range of lines if specified) to the\nscreen. See the command 'n' if line numbering is desired.\n");
  2407.     break;
  2408.     case 'q':
  2409.     add_message("Command: q    Usage: q\n");
  2410.     add_message("Quit the editor. Note that you can't quit this way if there\nare any unsaved changes.  See 'w' for writing changes to file.\n");
  2411.     break;
  2412.     case 'Q':
  2413.     add_message("Command: Q    Usage: Q\n");
  2414.     add_message("Force Quit.  Quit the editor even if the buffer contains unsaved\nmodifications.\n");
  2415.     break;
  2416.     case 'r':
  2417.     add_message("Command: r    Usage: r filename\n");
  2418.     add_message("Reads the given filename into the current buffer starting\nat the current line.\n");
  2419.     break;
  2420.     case 't':
  2421.     add_message("Command: t   Usage: tADDRESS or [range]tADDRESS\n");
  2422.     add_message("Transpose a copy of the current line (or range of lines if specified)\nto a location just after the specified ADDRESS.  Address 0 is the\nbeginning of the file and the default destination\nis the current line.\n");
  2423.     break;
  2424.     case 'v':
  2425.     add_message("Command: v   Usage: v/re/p\n");
  2426.     add_message("Search in all lines without expression 're', and print\n");
  2427.     add_message("every match. Other commands than 'p' can also be given\n");
  2428.     add_message("Compare with command 'g'.\n");
  2429.     break;
  2430.     case 'z':
  2431.     add_message("Command: z   Usage: z  or  z-  or z.\n");
  2432.     add_message("Displays 20 lines starting at the current line.\nIf the command is 'z.' then 20 lines are displayed being\ncentered on the current line. The command 'z-' displays\nthe 20 lines before the current line.\n");
  2433.     break;
  2434.     case 'Z':
  2435.     add_message("Command: Z   Usage: Z  or  Z-  or Z.\n");
  2436.     add_message("Displays 40 lines starting at the current line.\nIf the command is 'Z.' then 40 lines are displayed being\ncentered on the current line. The command 'Z-' displays\nthe 40 lines before the current line.\n");
  2437.     break;
  2438.     case 'x':
  2439.     add_message("Command: x   Usage: x\n");
  2440.     add_message("Save file under the current name, and then exit from ed.\n");
  2441.     break;
  2442.     case 's':
  2443.     if ( *inptr=='e' && *(inptr+1)=='t' ) {
  2444.         add_message("\
  2445. Without arguments: show current settings.\n\
  2446. 'set save' will preserve the current settings for subsequent invocations of ed.\n\
  2447. Options:\n\
  2448. \n\
  2449. number       will print line numbers before printing or inserting a lines\n\
  2450. list       will print control characters in p(rint) and z command like in l(ist)\n\
  2451. print       will show current line after a single substitution\n\
  2452. eightbit\n\
  2453. autoindent will preserve current indentation while entering text.\n");
  2454.         add_message("\
  2455.        use ^D or ^K to get back one step back to the right.\n\
  2456. excompatible will exchange the meaning of \\( and ( as well as \\) and )\n\
  2457. \n\
  2458. An option can be cleared by prepending it with 'no' in the set command, e.g.\n\
  2459. 'set nolist' to turn off the list option.\n\
  2460. \n\
  2461. set shiftwidth <digit> will store <digit> in the shiftwidth variable, which\n\
  2462. determines how much blanks are removed from the current indentation when\n\
  2463. typing ^D or ^K in the autoindent mode.\n");
  2464.         break;
  2465.     } else ;
  2466. /* is there anyone who wants to add an exact description for the 's' command? */
  2467.     case 'w':
  2468.     case 'W':
  2469.     case '/':
  2470.     case '?':
  2471.     add_message("Sorry no help yet for this command. Try again later.\n");
  2472.     break;
  2473.     default:
  2474.     add_message("       Help for Ed  (V 2.0)\n");
  2475.     add_message("---------------------------------\n");
  2476.     add_message("     by Qixx [Update: 7/10/91]\n");
  2477.     add_message("\n\nCommands\n--------\n");
  2478.     add_message("/\tsearch forward for pattern\n");
  2479.     add_message("?\tsearch backward for a pattern\n");
  2480.     /* add_message("^\tglobal search and print for pattern\n"); */
  2481.     add_message("=\tshow current line number\n");
  2482.     add_message("a\tappend text starting after this line\n");
  2483.     add_message("A\tlike 'a' but with inverse autoindent mode\n"),
  2484.     add_message("c\tchange current line, query for replacement text\n");
  2485.     add_message("d\tdelete line(s)\n");
  2486.     add_message("e\treplace this file with another file\n");
  2487.     add_message("E\tsame as 'e' but works if file has been modified\n");
  2488.     add_message("f\tshow/change current file name\n");
  2489.     add_message("g\tSearch and execute command on any matching line.\n");
  2490.     add_message("h\thelp file (display this message)\n");
  2491.     add_message("i\tinsert text starting before this line\n");
  2492.     add_message("I\tindent the entire file (from DGD ed v0.1)\n");
  2493.     add_message("\n--Return to continue--");
  2494.     P_MORE=1;
  2495.     break;
  2496.     }
  2497. }
  2498.  
  2499. static void print_help2() {
  2500.     P_MORE=0;
  2501.     add_message("j\tjoin lines together\n");
  2502.     add_message("k\tmark this line with a character - later referenced as 'a\n");
  2503.     add_message("l\tline line(s) with control characters displayed\n");
  2504.     add_message("m\tmove line(s) to specified line\n");
  2505.     add_message("n\ttoggle line numbering\n");
  2506.     add_message("p\tprint line(s) in range\n");
  2507.     add_message("q\tquit editor\n");
  2508.     add_message("Q\tquit editor even if file modified and not saved\n\
  2509. r\tread file into editor at end of file or behind the given line\n");
  2510.     add_message("s\tsearch and replace\n");
  2511.     add_message("set\tquery, change or save option settings\n");
  2512.     add_message("t\tmove copy of line(s) to specified line\n");
  2513.     add_message("v\tSearch and execute command on any non-matching line.\n");
  2514.     add_message("x\tsave file and quit\n");
  2515.     add_message("w\twrite to current file (or specified file)\n");
  2516.     add_message("W\tlike the 'w' command but appends instead\n");
  2517.     add_message("z\tdisplay 20 lines, possible args are . + -\n");
  2518.     add_message("Z\tdisplay 40 lines, possible args are . + -\n");
  2519.     add_message("\nFor further information type 'hc' where c is the command\nthat help is desired for.\n");
  2520. }
  2521.